package com.clouddms.service.security;

import com.clouddms.service.common.IService;
import com.clouddms.service.common.ServiceStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;

/**
 * 安全加密服务
 * 负责文件的AES加密/解密和MD5校验
 *
 * @author Cloud-DMS Team
 * @version 1.0.0
 */
@Slf4j
@Service
public class EncryptionService implements IService {

    @Autowired
    private KeyManagerService keyManagerService;

    @Autowired
    private FileIntegrityService fileIntegrityService;

    @Value("${clouddms.security.encryption.algorithm:AES}")
    private String algorithm;

    @Value("${clouddms.security.encryption.enabled-extensions:pdf,doc,docx,xls,xlsx}")
    private String enabledExtensions;

    // 使用更安全的CBC模式替代ECB
    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
    private static final int BUFFER_SIZE = 8192;
    private static final int IV_LENGTH = 16; // AES块大小
    private final SecureRandom secureRandom = new SecureRandom();

    // === ICloudDmsService 接口实现 ===

    @Override
    public String getServiceName() {
        return "securityEncrypt";
    }

    @Override
    public String getDisplayName() {
        return "安全加密模块";
    }

    @Override
    public String getDescription() {
        return "使用AES加密算法保护敏感文件，MD5校验文件完整性，确保数据安全";
    }

    @Override
    public ServiceStatus getStatus() {
        return ServiceStatus.RUNNING;
    }

    @Override
    public int getPriority() {
        return 30; // 优先级较高，安全模块很重要
    }

    // === 原有的业务方法 ===

    /**
     * 检查文件类型是否需要加密
     *
     * @param fileName 文件名
     * @return 是否需要加密
     */
    public boolean needsEncryption(String fileName) {
        if (fileName == null || !fileName.contains(".")) {
            return false;
        }

        String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
        List<String> enabledList = Arrays.asList(enabledExtensions.split(","));

        boolean needs = enabledList.contains(extension);
        log.debug("文件 {} 扩展名 {} 是否需要加密: {}", fileName, extension, needs);

        return needs;
    }

    /**
     * 加密文件（带用户密钥）
     *
     * @param inputFile 原文件路径
     * @param outputFile 加密后文件路径
     * @param userId 用户ID
     * @return 加密结果信息
     */
    public EncryptionResult encryptFile(String inputFile, String outputFile, Long userId) {
        try {
            log.info("开始加密文件: {} -> {}, 用户ID: {}", inputFile, outputFile, userId);

            // 获取用户专用密钥
            SecretKey userKey = keyManagerService.getUserFileKey(userId);

            // 生成随机IV
            byte[] iv = new byte[IV_LENGTH];
            secureRandom.nextBytes(iv);
            IvParameterSpec ivSpec = new IvParameterSpec(iv);

            // 创建加密器
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, userKey, ivSpec);

            // 执行加密
            boolean success = processFileWithIV(inputFile, outputFile, cipher, iv);
            
            if (success) {
                String encryptedHash = calculateMD5(outputFile);
                return EncryptionResult.success(encryptedHash, iv, keyManagerService.encodeKey(userKey));
            } else {
                return EncryptionResult.failure("加密处理失败");
            }

        } catch (Exception e) {
            log.error("文件加密失败: {}", e.getMessage(), e);
            return EncryptionResult.failure("加密异常: " + e.getMessage());
        }
    }

    /**
     * 兼容旧版本的加密方法
     */
    public boolean encryptFile(String inputFile, String outputFile) {
        EncryptionResult result = encryptFile(inputFile, outputFile, 1L); // 默认用户ID
        return result.isSuccess();
    }

    /**
     * 解密文件（使用用户密钥和IV）
     *
     * @param inputFile 加密文件路径
     * @param outputFile 解密后文件路径
     * @param userId 用户ID
     * @param ivData IV数据
     * @return 是否解密成功
     */
    public boolean decryptFile(String inputFile, String outputFile, Long userId, byte[] ivData) {
        try {
            log.info("开始解密文件: {} -> {}, 用户ID: {}", inputFile, outputFile, userId);

            // 获取用户专用密钥
            SecretKey userKey = keyManagerService.getUserFileKey(userId);

            // 创建IV规范
            IvParameterSpec ivSpec = new IvParameterSpec(ivData);

            // 创建解密器
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, userKey, ivSpec);

            // 执行解密（跳过文件头的IV数据）
            return processDecryptedFile(inputFile, outputFile, cipher);

        } catch (Exception e) {
            log.error("文件解密失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 兼容旧版本的解密方法
     */
    public boolean decryptFile(String inputFile, String outputFile) {
        try {
            // 从文件中读取IV
            byte[] iv = readIVFromFile(inputFile);
            return decryptFile(inputFile, outputFile, 1L, iv);
        } catch (Exception e) {
            log.error("解密文件失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 计算文件MD5哈希值（使用FileIntegrityService）
     *
     * @param filePath 文件路径
     * @return MD5哈希值，失败返回null
     */
    public String calculateMD5(String filePath) {
        try {
            log.debug("计算文件MD5: {}", filePath);

            FileIntegrityService.FileIntegrityInfo integrity = 
                fileIntegrityService.calculateFileIntegrity(filePath, FileIntegrityService.HashAlgorithm.MD5);
            
            if (integrity.isSuccess()) {
                String md5 = integrity.getHash(FileIntegrityService.HashAlgorithm.MD5);
                log.debug("文件 {} MD5值: {}", filePath, md5);
                return md5;
            } else {
                log.warn("计算MD5失败: {}", integrity.getErrorMessage());
                return null;
            }

        } catch (Exception e) {
            log.error("计算MD5失败: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 计算文件的多种哈希值
     *
     * @param filePath 文件路径
     * @param algorithms 哈希算法数组
     * @return 文件完整性信息
     */
    public FileIntegrityService.FileIntegrityInfo calculateFileHashes(String filePath, 
                                                                     FileIntegrityService.HashAlgorithm... algorithms) {
        return fileIntegrityService.calculateFileIntegrity(filePath, algorithms);
    }

    /**
     * 验证文件完整性（MD5）
     *
     * @param filePath 文件路径
     * @param expectedMD5 期望的MD5值
     * @return 文件是否完整
     */
    public boolean verifyFileIntegrity(String filePath, String expectedMD5) {
        FileIntegrityService.IntegrityVerificationResult result = 
            fileIntegrityService.verifyFileIntegrity(filePath, expectedMD5, FileIntegrityService.HashAlgorithm.MD5);
        
        return result.isValid();
    }

    /**
     * 验证文件完整性（指定算法）
     *
     * @param filePath 文件路径
     * @param expectedHash 期望的哈希值
     * @param algorithm 哈希算法
     * @return 验证结果
     */
    public FileIntegrityService.IntegrityVerificationResult verifyFileIntegrityWithAlgorithm(
            String filePath, String expectedHash, FileIntegrityService.HashAlgorithm algorithm) {
        return fileIntegrityService.verifyFileIntegrity(filePath, expectedHash, algorithm);
    }

    /**
     * 带IV的文件处理方法
     */
    private boolean processFileWithIV(String inputFile, String outputFile, Cipher cipher, byte[] iv) {
        try (FileInputStream fis = new FileInputStream(inputFile);
             FileOutputStream fos = new FileOutputStream(outputFile)) {

            // 先写入IV到文件头
            fos.write(iv);

            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;

            while ((bytesRead = fis.read(buffer)) != -1) {
                byte[] processedBytes;

                if (bytesRead == BUFFER_SIZE) {
                    processedBytes = cipher.update(buffer);
                } else {
                    // 最后一块数据
                    byte[] lastBlock = new byte[bytesRead];
                    System.arraycopy(buffer, 0, lastBlock, 0, bytesRead);
                    processedBytes = cipher.doFinal(lastBlock);
                }

                if (processedBytes != null) {
                    fos.write(processedBytes);
                }
            }

            log.info("带IV的文件处理完成: {} -> {}", inputFile, outputFile);
            return true;

        } catch (Exception e) {
            log.error("带IV的文件处理失败: {}", e.getMessage(), e);
            // 清理可能产生的不完整文件
            try {
                Files.deleteIfExists(Paths.get(outputFile));
            } catch (Exception ex) {
                log.warn("清理临时文件失败: {}", ex.getMessage());
            }
            return false;
        }
    }

    /**
     * 解密文件处理方法（跳过IV）
     */
    private boolean processDecryptedFile(String inputFile, String outputFile, Cipher cipher) {
        try (FileInputStream fis = new FileInputStream(inputFile);
             FileOutputStream fos = new FileOutputStream(outputFile)) {

            // 跳过文件头的IV数据
            byte[] ivBuffer = new byte[IV_LENGTH];
            int ivBytesRead = fis.read(ivBuffer);
            if (ivBytesRead != IV_LENGTH) {
                throw new IOException("无法读取完整的IV数据");
            }

            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;

            while ((bytesRead = fis.read(buffer)) != -1) {
                byte[] processedBytes;

                if (bytesRead == BUFFER_SIZE) {
                    processedBytes = cipher.update(buffer);
                } else {
                    // 最后一块数据
                    byte[] lastBlock = new byte[bytesRead];
                    System.arraycopy(buffer, 0, lastBlock, 0, bytesRead);
                    processedBytes = cipher.doFinal(lastBlock);
                }

                if (processedBytes != null) {
                    fos.write(processedBytes);
                }
            }

            log.info("解密文件处理完成: {} -> {}", inputFile, outputFile);
            return true;

        } catch (Exception e) {
            log.error("解密文件处理失败: {}", e.getMessage(), e);
            // 清理可能产生的不完整文件
            try {
                Files.deleteIfExists(Paths.get(outputFile));
            } catch (Exception ex) {
                log.warn("清理临时文件失败: {}", ex.getMessage());
            }
            return false;
        }
    }

    /**
     * 从加密文件中读取IV
     */
    private byte[] readIVFromFile(String filePath) throws IOException {
        try (FileInputStream fis = new FileInputStream(filePath)) {
            byte[] iv = new byte[IV_LENGTH];
            int bytesRead = fis.read(iv);
            if (bytesRead != IV_LENGTH) {
                throw new IOException("无法读取完整的IV数据");
            }
            return iv;
        }
    }

    /**
     * 处理文件加密/解密
     */
    private boolean processFile(String inputFile, String outputFile, Cipher cipher) {
        try (FileInputStream fis = new FileInputStream(inputFile);
             FileOutputStream fos = new FileOutputStream(outputFile)) {

            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;

            while ((bytesRead = fis.read(buffer)) != -1) {
                byte[] processedBytes;

                if (bytesRead == BUFFER_SIZE) {
                    processedBytes = cipher.update(buffer);
                } else {
                    // 最后一块数据
                    byte[] lastBlock = new byte[bytesRead];
                    System.arraycopy(buffer, 0, lastBlock, 0, bytesRead);
                    processedBytes = cipher.doFinal(lastBlock);
                }

                if (processedBytes != null) {
                    fos.write(processedBytes);
                }
            }

            log.info("文件处理完成: {} -> {}", inputFile, outputFile);
            return true;

        } catch (Exception e) {
            log.error("文件处理失败: {}", e.getMessage(), e);

            // 清理可能产生的不完整文件
            try {
                Files.deleteIfExists(Paths.get(outputFile));
            } catch (Exception ex) {
                log.warn("清理临时文件失败: {}", ex.getMessage());
            }

            return false;
        }
    }

    /**
     * 获取加密算法信息
     */
    public String getEncryptionInfo() {
        return String.format("算法: %s, 转换: %s", algorithm, TRANSFORMATION);
    }
}